在看完 UI 外層的 Window 及 MenuBar 後,接下來持續往內看其他元件。在刻 UI 時,最常需要處理的應該就是「文字」吧?今天的耕讀筆記就以 Text 相關的元件為主軸,研究一下其使用及設定方式。
Text 元件要在 UI 上顯示文字,不論是一句或是多行,都可以使用 Text 元件。雖然 Text 元件有高達十六個參數可以設定,但除了 text(設定要顯示的文字)參數外,其餘全部都有預設值,所以要在 UI 上放文字只需要非常精簡的一段程式碼:
// 宣告一個文字元件
Text("Hello, World")
// 元件完整參數示意
Text(
    text = "...",
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current
)
若是要微調樣式裡的細節,可以透過以上這些參數來做設定:
text:設定要顯示的文字。modifier:調整 Text 屬性。color:設定文字顏色。fontSize:設定文字大小。fontStyle:設定文字風格,如斜體。fontWeight:設定文字粗細。fontFamily:設定文字字體。letterSpacing:設定文字的字元間距。textDecoration:設定文字裝飾,如底線。textAlign:設定文字對齊方式。lineHeight:設定文句的行距。overflow:設定當文字字數超過文字框範圍時的效果。softWrap:設定文字是否折行,預設為 true。maxLines:設定文字框行數上限。onTextLayout:設定監聽當文字框發生變化時的 Event Handler。style:文字樣式設定。這個設定與上面各屬性重疊,開發者可以選擇偏好的樣式設定方式。其實 Text 的基礎樣式已遵守 Material Design 的設計原則,若你有客製化的需求,可以用更低階的 BasicText 再做延伸。
color)想要調整文字顏色,可以傳入 androidx.compose.ui.graphics.Color 實例當參數。設定 Color 時,可直接傳入 32-bit ARGB 顏色整數值,如 0xFF00FFFF,或是直接用預設色板,如 Color.Cyan。
Text(
    text = "...",
    color = Color(0xFF00FFFF)
)
Text(
    text = "...",
    color = Color.Cyan
)
Color 類別的預設色板有:
Color.Black:黑色。Color.DarkGray:深灰色。Color.Gray:灰色。Color.LightGray:亮灰色。Color.White:白色。Color.Red:紅色。Color.Green:綠色。Color.Blue:藍色。Color.Yellow:黃色。Color.Cyan:青色。Color.Magenta:品紅色。Color.Transparent:透明。fontSize)想要調整文字大小,可直接設定 20.sp 這種較為精簡的語法,或是傳入 androidx.compose.ui.unit.TextUnit 實例當參數。在設定 TextUnit 時,需傳入型別為 Float 的數值(如 20F)及設定 TextUnitType(如 TextUnitType.Sp)。在使用 TextUnit 時,由於用到 Experimental API,記得要標記 @OptIn(ExperimentalUnitApi::class) Annotation。
Text(
    text = "...",
    fontSize = 20.sp
)
Text(
    text = "...",
    fontSize = TextUnit(20F, TextUnitType.Sp)
)
fontStyle)fontStyle 只有兩種風格:一般(Normal)及斜體(Italic)。想要設定文字風格,可傳入 androidx.compose.ui.text.font.FontStyle 實例當參數,傳入 0 就是一般、傳入 1 就是斜體。或是直接用 Preset 更具可讀性。
Text(
    text = "...",
    fontStyle = FontStyle(0) // 等同 FontStyle.Normal
)
Text(
    text = "...",
    fontStyle = FontStyle.Italic
)
fontWeight)想要調整文字粗細,可以傳入 androidx.compose.ui.text.font.FontWeight 實例當參數。設定 FontWeight 時,可直接傳入整數值,或是直接用 Preset。
Text(
    text = "...",
    fontWeight = FontWeight(1000)
)
Text(
    text = "...",
    fontWeight = FontWeight.Thin
)
FontWeight 類別的 Preset 有:
Thin
ExtraLight
Light
Normal
Medium
SemiBold
Bold
ExtraBold
Black
W100、W200、W300 到 W900
fontFamily)想要設定文字字體,傳入 androidx.compose.ui.text.font.FontFamily 的 Preset 即可。
Text(
    text = "...",
    fontFamily = FontFamily.Default
)
Text(
    text = "...",
    fontFamily = FontFamily.Monospace
)
FontFamily 類別的 Preset 有:
Default
SansSerif
Serif
Monospace
Cursive
textDecoration)想要設定文字裝飾,傳入 androidx.compose.ui.text.style.TextDecoration 的 Preset 即可。
Text(
    text = "...",
    textDecoration = TextDecoration.Underline
)
Text(
    text = "...",
    textDecoration = TextDecoration.LineThrough
)
TextDecoration 類別的 Preset 有:
None:沒有文字裝飾。Underline:底線。LineThrough:刪除線。textAlign)想要設定文字對齊方式,傳入 androidx.compose.ui.text.style.TextAlign 的 Preset 即可。
Text(
    text = "...",
    modifier = Modifier.fillMaxWidth(),
    textAlign = TextAlign.Left,
)
Text(
    text = "...",
    modifier = Modifier.fillMaxWidth(),
    textAlign = TextAlign.Center,
)
Text(
    text = "...",
    modifier = Modifier.fillMaxWidth(),
    textAlign = TextAlign.Right
)
在上面的例子裡,傳入 Modifier 參數讓文字框撐大到跟 Window 一樣寬,才能看到文字對齊的視覺效果。關於 Modifier 的用法,之後會有再另外深入研究,現階段先暫時 Copy & Paste 使用即可。TextAlign 類別的 Preset 有:
Left:置左對齊。Right:置右對齊。Center:置中對齊。Justify:左右對齊。Start:從頭對齊。End:從尾對齊。letterSpacing、lineHeight、overflow、softWrap、maxLines)許多時候 UI 顯示的不會只是一小段文字,而是一整篇文章,這時要控制的就是一整段文字,可以把 Text 視為文字框來做更多文字排版的設定。
Text(
    text = "...",
    letterSpacing = 5.sp,
    lineHeight = 10.sp,
    overflow = TextOverflow.Ellipsis,
    softWrap = true,
    maxLines = 3,
)
在上面的範例裡,letterSpacing 及 lineHeight 都是傳入 TextUnit 類別,用 數值 + sp 設定即可。softWrap 設定了當文字超過文字框的範圍時要不要折行,maxLines 是與 overflow 搭配,由 maxLines 決定文字框的高度是幾行,若文字字數超過文字框所能顯示的行數,則由 overflow 決定超過時要怎麼顯示。
TextOverflow 類別的顯示方式共有:
Clip:強制截斷。Ellipsis:超過的部份截斷,並在結尾加上 ...。Visible:強制顯示,文字會超出文字框破壞版面。AnnotatedString 排出混合多種樣式及標記的文字在排版大量文字時,比方說一篇文章,往往會需要混合多種樣式,比方說標題、粗體、底線,甚至想要標記文字裡面的超連結,使用單純的 String 並無法滿足需求。若遇到這樣的情境,我們可以使用 Compose 的 AnnotatedString。
AnnotatedString 是一個 Data Class,除了儲存文字內容外,還包含 SpanStyle 和 ParagraphStyle 的 Range 清單。SpanStyle 用於標記在字串的文字樣式,ParagraphStyle 用於標記字串的段落樣式,Range 則是確定各字串的範圍。我們可以使用 buildAnnotatedString() 函式,以 DSL 的方式建立 AnnotatedString,並用 append 增加文字進字串,withStyle 則可用於指定文字或段落的樣式。
另外,我們還可以用 pushStringAnnotation() 搭配 tag 及 annotation 參數來增加標記直到呼叫 pop() 結束,這些標記可以用來與點擊事件搭配做互動。
Text(
    text = buildAnnotatedString {
        withStyle(
            style = SpanStyle(
                color = Color.Black,
                fontSize = 30.sp,
            )
        ) {
            append("Click ")
        }
        pushStringAnnotation(
            tag = "URL",
            annotation = "https://kotlin.tips"
        )
        withStyle(
            style = SpanStyle(
                color = Color.Blue,
                fontSize = 30.sp,
                fontWeight = FontWeight.Bold,
                textDecoration = TextDecoration.Underline
            )
        ) {
            append("HERE")
        }
        pop()
    }
)
預設 Text 對點擊事件是沒有反應的,若需要做出如超連結的文字點擊互動,可以用 ClickedText。在使用 ClickedText 元件時,傳入的 text 參數就得是 AnnotatedString,再綁定 onClick 事件,就可以做出超連結的效果:
val annotatedText = buildAnnotatedString { /* ... */ }
ClickableText(
    text = annotatedText,
    onClick = { offset ->
        annotatedText.getStringAnnotations(
            tag = "URL",
            start = offset,
            end = offset
        ).firstOrNull()?.let { annotation ->
                println("Go to ${annotation.item}")
            }
    }
)
Compose 元件是可以互相堆疊與組合使用的,由於許多地方都會用到 Text 元件(比方說在 Button 裡),因此預設 Text 是不能被選取的(防止按鈕上的文字也會被選取,影響其他按鈕互動)。不過在一些情境下,比方說顯示一整篇的文章時,我們會需要讓 Text 裡的文字可以被選取。
要讓 Text 的文字可以被選取,只要在外層加一個 SelectionContainer 即可,只要被這個容器包住的 Text 元件就能夠被選取。反之,若不想元件內的 Text 被選取,則可用 DisableSelection 包住。甚至這兩個容器還可以互動包夾混用:
Column {
    SelectionContainer {
        Column {
            Text("I'm SELECTABLE")
            DisableSelection {
                Text(
                    text = "I'm NOT selectable"
                )
            }
            Text("I'm SELECTABLE")
        }
    }
    
    Text(
        text = "I'm NOT selectable"
    )
}
從上例可以看出,單獨的 Text 是不能被選取的,而被 SelectionContainer 包住的 Text 則可以,但在 SelectionContainer 容器裡被 DisableSelection 包住的 Text 也是不能被選取的。透過這兩個容器的運用,就可以明確界定哪些文字可選取,哪些不行了。
這跟本 Text 的百科全書吧! 推~
因為還在初學,所以只好地毯式的掃過一次各元件,進階的還要靠你多分享了 ?